package io.task.cc.network.maintain;

import io.task.cc.network.ChannelContext;
import io.task.cc.network.utils.lock.LockUtils;
import io.task.cc.network.utils.lock.LockedMap;
import io.task.cc.network.utils.lock.LockedSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * 一对多  (token <--> ChannelContext)<br>
 */
public class Tokens {
    private static final Logger log = LoggerFactory.getLogger(Tokens.class);

    /**
     * key: token
     * value: SetWithLock<ChannelContext>
     */
    private final LockedMap<String, LockedSet<ChannelContext>> map = new LockedMap<>(new HashMap<String, LockedSet<ChannelContext>>());

    private final Object synLockObj1 = new Object();

    /**
     * 绑定token.
     *
     * @param token          the token
     * @param channelContext the channel context
     * @author tanyaowu
     */
    public void bind(String token, ChannelContext channelContext) {


        if (token == null) {
            return;
        }

        try {
            LockedSet<ChannelContext> lockedSet = map.get(token);
            if (lockedSet == null) {
                LockUtils.runWriteOrWaitRead("_tio_tokens_bind__" + token, synLockObj1, () -> {
//					@Override
//					public void read() {
//					}

//					@Override
//					public void write() {
//						SetWithLock<ChannelContext> setWithLock  = mapWithLock.get(token);
                    if (map.get(token) == null) {
//							setWithLock = new SetWithLock<>(new HashSet<>());
                        map.put(token, new LockedSet<>(new HashSet<>()));
                    }
//					}
                });
                lockedSet = map.get(token);
            }
            lockedSet.add(channelContext);

            channelContext.setToken(token);
        } catch (Throwable e) {
            log.error("", e);
        } finally {
            //			lock.unlock();
        }

    }


    public LockedSet<ChannelContext> find(String token) {


        if (token == null) {
            return null;
        }
        String key = token;
        Lock lock = map.readLock();
        lock.lock();
        try {
            Map<String, LockedSet<ChannelContext>> m = map.getT();
            return m.get(key);
        } catch (Throwable e) {
            throw e;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 解除channelContext绑定的token
     *
     * @param channelContext the channel context
     */
    public void unbind(ChannelContext channelContext) {

        try {
            String token = channelContext.getToken();
            if (token == null) {
                log.debug("{}, {}, 并没有绑定Token", channelContext.config.getName(), channelContext);
                return;
            }

            try {
                LockedSet<ChannelContext> set = map.get(token);
                if (set == null) {
                    log.warn("{}, {}, token:{}, 没有找到对应的SetWithLock", channelContext.config.getName(), channelContext, token);
                    return;
                }
                channelContext.setToken(null);
                set.remove(channelContext);

                if (set.size() == 0) {
                    map.remove(token);
                }
            } catch (Throwable e) {
                throw e;
            }
        } catch (Throwable e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 解除tioConfig范围内所有ChannelContext的 token绑定
     */
    public void unbind(String token) {

        if (token == null) {
            return;
        }

        try {
            LockedSet<ChannelContext> set1 = map.get(token);
            if (set1 == null) {
                return;
            }

            WriteLock writeLock = set1.writeLock();
            writeLock.lock();
            try {
                Set<ChannelContext> set = set1.getT();
                if (set.size() > 0) {
                    for (ChannelContext channelContext : set) {
                        channelContext.setToken(null);
                    }
                    set.clear();
                }

                map.remove(token);
            } catch (Throwable e) {
                log.error(e.getMessage(), e);
            } finally {
                writeLock.unlock();
            }
        } catch (Throwable e) {
            throw e;
        }
    }
}
